#include "module.hh"

#include <memory>
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

auto Module::add_output(Rc<Module> module) -> void
{
    this->_outputs.emplace_back(module);
}

auto Module::add_intput(Rc<Module> module) -> void
{
    this->_inputs.emplace_back(module);
}

auto Module::print() -> void
{
    std::cout << this->complete_name()
              << " -> [";
    std::for_each(this->_outputs.begin(), this->_outputs.end(), [] (Rc<Module> module) {
        std::cout << module->complete_name() << ", ";
    });
    std::cout << ']' << std::endl;
}

auto Module::show_pulse_outputs() -> void
{
    const char* pulse {
        this->_state == ModuleState::Low ? "-low->" : "-high->"
    };
    std::for_each(this->_outputs.cbegin(), this->_outputs.cend(), 
        [this, pulse] (Rc<Module> next) {
            std::cout << this->name() << ' ' 
                        << pulse << ' '
                        << next->name() << std::endl;
        }
    );
}

// auto Broadcaster::update() -> bool
// {
//     this->show_pulse_outputs();
//     return true;
// }

// auto Broadcaster::complete_name() -> String
// {
//     return "broadcaster";
// }

auto Flipflop::update() -> bool
{
    Rc<Module> input {this->input()};
    return this->force_update(input->state());
}

auto Flipflop::complete_name() -> String
{
    return "%" + this->_name;
}

auto Flipflop::force_update(ModuleState state) -> bool
{
    if (state == ModuleState::Low)
    {
        if (this->_ffstate == FlipflopState::Off)
        {
            this->_ffstate = FlipflopState::On;
            this->_state = ModuleState::High;
        }
        else
        {
            this->_ffstate = FlipflopState::Off;
            this->_state = ModuleState::Low;
        }

        this->show_pulse_outputs();
        return true;
    }

    // Input High, Nothing happend
    return false;
}

auto Conjunction::update() -> bool 
{
    // Check all Conjunction's inputs
    ModuleState next {
        std::all_of(this->_inputs.begin(), this->_inputs.end(), [](Rc<Module> input){
            return input->state() == ModuleState::High;
        }) ? ModuleState::Low : ModuleState::High
    };

    if (this->_state != next)
    {
        this->_state = next;
        this->show_pulse_outputs();
        return true;
    }

    return false;
}

auto Conjunction::complete_name() -> String
{
    return "&" + this->_name;
}

auto Output::update() -> bool 
{
    return false;
}

auto Output::complete_name() -> String
{
    return this->_name;
}